/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.oimwrapper.miscadapters;

import Thor.API.Operations.*;
import Thor.API.tcResultSet;
import Thor.API.tcUtilityFactory;
import com.thortech.util.logging.Logger;
import com.thortech.xl.dataaccess.tcDataProvider;
import java.util.*;
import org.oimwrapper.connectors.internal.OIMFormUtilities;
import org.oimwrapper.connectors.internal.OIMLookupUtilities;
import org.oimwrapper.connectors.internal.OIMObjectFormUtilities;
import org.oimwrapper.connectors.internal.OIMUtilityGetter;

/**
 *
 */
public class Assignment {

    // field in the parent table that stores the key of the child group currently waiting for approval
    private static final String GROUPAPPROVALKEY = "GROUPAPPROVALKEY";
    
    private tcFormInstanceOperationsIntf formInstOps = null;
    private tcFormDefinitionOperationsIntf formDefOps = null;
    private tcObjectOperationsIntf objectOps;
    private tcLookupOperationsIntf lookupOps;
    
    private String assignmentType;
    private String assignmentKey;

    private String assignedToUser;
    private String assignedToGroup;
    private String assignedToType;
    private tcDataProvider database;
    
    private static Logger logger;
    private static Logger fwLogger = Logger.getLogger("FWLOGGER");
    

    public Assignment() {
        logger = Logger.getLogger(Assignment.class.getName());
    }
    
    public Assignment(String LogCategory,tcDataProvider database) {
        logger = Logger.getLogger(LogCategory);
        this.database = database;
    }

    public void dummyMethod()
    {
        
    }

    public void setDatabase(tcDataProvider database)
    {
        this.database = database;
    }
    
    public String getAssignmentKey() {
        logger.debug("assignmentKey:" + assignmentKey);
        return assignmentKey;
    }

    public String getAssignmentType() {
        logger.debug("assignmentType:" + assignmentType);
        return assignmentType;
    }

    public void setAssignedToType(String assignedToType) {
        this.assignedToType = assignedToType;
        logger.debug("assignedToType:" + this.assignedToType);
    }

    public void setAssignedToUser(String assignedToUser) {
        this.assignedToUser = assignedToUser;
        logger.debug("assignedToUser:" + this.assignedToUser);
    }

    public void setAssignedToGroup(String assignedToGroup) {
        this.assignedToGroup = assignedToGroup;
        logger.debug("assignedToGroup:" + this.assignedToGroup);
    }
    
    public String intializeOps()
    {
        try
        {
            formInstOps = (tcFormInstanceOperationsIntf)tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcFormInstanceOperationsIntf");
            formDefOps = (tcFormDefinitionOperationsIntf)tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcFormDefinitionOperationsIntf");
            objectOps = (tcObjectOperationsIntf)tcUtilityFactory.getUtility(database,"Thor.API.Operations.tcObjectOperationsIntf");
            lookupOps = (tcLookupOperationsIntf)tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcLookupOperationsIntf");
        }
        catch(Exception e)
        {
            logger.error("Init Failed",e);
            return "FAILURE";
        }
        return "SUCCESS";
        
    }
    /*
     * set the defaults based on the task assignment tab
     */
    private void setDefaults()
    {
        assignmentType = assignedToType;
        Map findMap = new HashMap();
        List returnFlds = new ArrayList();
        
        if (assignedToType.equalsIgnoreCase("user"))
        {
            try
            {
                findMap.put("Users.User ID",assignedToUser);
                returnFlds.add("Users.Key");
                Map usr = this.getUserDataSelected(findMap,returnFlds);
                assignmentKey = (String)usr.get("Users.Key");
            }
            catch(Exception e)
            {
                logger.error("API Failure",e);
            }
            
        }
        if (assignedToType.equalsIgnoreCase("group"))
        {
            try
            {
                String key = this.findGroup(assignedToGroup);
                assignmentKey = key;
            }
            catch(Exception e)
            {
                logger.error("API Failure",e);
            }
        }
    }
    
    /*
     * assign this users manager
     */
    public String assignManager(String userId)
    {
        Map findMap = new HashMap();
        List returnFlds = new ArrayList();
        logger.debug("Assign Manager for " + userId);
        try
        {
            findMap.put("Users.User ID",userId);
            returnFlds.add("Users.Manager Key");
            Map usr = this.getUserDataSelected(findMap,returnFlds);
            assignmentType = "user";
            assignmentKey = (String)usr.get("Users.Manager Key");
            if (assignmentKey == null || assignmentKey.trim().length()==0)
            {
                logger.error("User Has no manager " + userId);
                setDefaults();
            }
            return "SUCCESS";

        }
        catch(Exception e)
        {
            logger.error("Manager Assignment Failed for user " + userId);
            return "FAILURE";
        }
    }

    /*
     * assign this task to this groupkey
     */
    public String assignGroup(String groupKey)
    {
        logger.debug("Assign Group for " + groupKey);
        assignmentKey = groupKey;
        assignmentType = "group";
        return "SUCCESS";
    }

    /*
     * assign this users manager to the task
     */
    public String assignManager(long userKey)
    {
        Map findMap = new HashMap();
        List returnFlds = new ArrayList();
        
        try
        {
            findMap.put("Users.Key",new Long(userKey).toString());
            returnFlds.add("Users.Manager Key");
            Map usr = this.getUserDataSelected(findMap,returnFlds);
            assignmentType = "user";
            assignmentKey = (String)usr.get("Users.Manager Key");
            if (assignmentKey == null || assignmentKey.trim().length()==0)
            {
                logger.error("User Has no manager " + userKey);
                setDefaults();
            }
            
            return "SUCCESS";
        }
        catch(Exception e)
        {
            logger.error("Manager Assignment Failed for user " + userKey);
            return "FAILURE";
        }
    }

    /*
     * checks to see if the last approver of the approval process is specified in the
     * process form. if it is then assign this person/group as the assignee
     */
    
    public String assignLastApprover(long processInstanceKey)
    {
        logger.debug("assignLastApprover");
        try
        {
            
            Map<String,String> processForm = getApprovalFields(processInstanceKey);
            if (processForm == null)
            {
                logger.error("No Process Data Found");
                setDefaults();
                return "SUCCESS";
            }
            
            logger.debug("Process Form Returned");
            String lastApproverType = processForm.get("LASTAPPROVER_TYPE");
            String lastApproverId = processForm.get("LASTAPPROVER_ID");
            
            logger.debug("lastApproverId:" + lastApproverId);
            logger.debug("lastApproverType:" + lastApproverType);
            
            String errmsg = null;
            
            if (lastApproverType == null || lastApproverType.trim().length() <= 0)
            {
                logger.error("Invalid lastApproverType " + lastApproverType);
                errmsg = "ERROR " + "Invalid lastApproverType " + lastApproverType;
            }
            if (lastApproverId == null || lastApproverId.trim().length() <= 0)
            {
                logger.error("Invalid lastApproverId " + lastApproverId);
                errmsg = "ERROR " + "Invalid lastApproverId " + lastApproverId;
            }
            if (errmsg == null)
            {
                // override defaults
                this.assignedToType = lastApproverType;
                this.assignedToGroup = lastApproverId;
                this.assignedToUser = lastApproverId;
            }
            logger.debug("Setting defaults " +assignedToType + ":" + assignedToGroup);
            setDefaults();
            
        }
        catch(Exception e)
        {
            logger.error("Error finding assignment",e);
            return "Error finding assignment " + e.getMessage();
        }
        return "SUCCESS";
    }
    
    /*
     * checks to see if the last approver of the approval process is specified in the
     * process form. if it is then assign this person/group as the assignee
     * the child approvals take precedence over the parent approval
     */
    
    public String assignLastGroupApprover(long processInstanceKey,String resourceName)
    {
        logger.debug("assignLastGroupApprover " + processInstanceKey + ":" + resourceName);
        try
        {
            
            Map<String,String> processForm = getApprovalFields(processInstanceKey);
            
            
            if (processForm == null)
            {
                logger.error("No Process Data Found");
                setDefaults();
                return "SUCCESS";
            }
            logger.debug("Parent " + processForm);
            
            String lastApproverType = null;
            String lastApproverId = null;
            Stack groupApprovalList = getChildApprovers(processInstanceKey,resourceName);
            if (groupApprovalList == null)
            {
                logger.debug("groupApprovalList is null. setting default");
                Map tmpMap = new HashMap();
                tmpMap.put("LASTAPPROVER_ID","SYSTEM ADMINISTRATORS");
                tmpMap.put("LASTAPPROVER_TYPE","group");
                groupApprovalList = new Stack();
                groupApprovalList.push(tmpMap);
            }
            List provisioningGroups = this.getProvisioningGroups();
            boolean haveApproval = false;
            for(int i=0;i<groupApprovalList.size();i++)
            {
                Map approval = (Map)groupApprovalList.pop();
                String approvalId = (String)approval.get("LASTAPPROVER_ID");
                if(provisioningGroups.contains(approvalId))
                {
                   lastApproverType = (String)approval.get("LASTAPPROVER_TYPE");
                   lastApproverId = approvalId;
                   haveApproval = true;
                   break;
                }
            }
            
            // child approval takes precidence over parent approval
            if (haveApproval)
            {
                // override defaults
                logger.debug("Child Approval Override");
                this.assignedToType = lastApproverType;
                this.assignedToGroup = lastApproverId;
                this.assignedToUser = lastApproverId;
                logger.debug("Setting defaults " +assignedToType + ":" + assignedToGroup);
                setDefaults();
                return "SUCCESS";
            }
            
            return this.assignLastApprover(processInstanceKey);
            
        }
        catch(Exception e)
        {
            logger.error("Error finding assignment",e);
            return "Error finding assignment " + e.getMessage();
        }
    }
    
    /*
     * this checks to see if one of the child entries requires an approval
     * if it does it uses the resource name and the group name to find an approver
     * if it cant find one it uses the default values set on the task for the assignment
     * we also track this approval by storing the key to this child in the parent.
     */
    public String assignGroupApprover(String objectInstanceKey,String resourceName)
    {
        
        
        logger.debug("assignGroupApprover");
        logger.debug("resource " + resourceName);
        logger.debug("obi " + objectInstanceKey);
        
        try
        {
            
            String childName = getChildFormNameForResource(resourceName);
            if (childName == null)
            {
                logger.error("Child Table not Found for resource " + resourceName);
                return "FAILURE";
            }
            
            String groupField = getChildGroupField(resourceName);
            if (groupField == null)
            {
                logger.error("Child Table groupField not Found for resource " + resourceName);
                return "FAILURE";
            }
            
            OIMObjectFormUtilities formUtils = OIMUtilityGetter.getObjectFormUtilities(fwLogger, objectOps,formInstOps, formDefOps);
            
            /*
             * get the name of the field in the parent that holds our child key value.
             */
            Map reqObj = formUtils.getObjectFormValues(Long.parseLong(objectInstanceKey), true, null);
            Set<String> keys = reqObj.keySet();
            Iterator<String> i = keys.iterator();
            String groupKeyFieldName = null;
            while(i.hasNext())
            {
                String k = i.next();
                String v = (String)reqObj.get(k);
                if (k.contains(GROUPAPPROVALKEY))
                {
                    groupKeyFieldName = k;
                    break;
                }
                    
                logger.debug(k + ":" + v);
            }
            
            /*
             * get all the child records
             */
            Map[] reqChildren = formUtils.getObjectFormChildValues(Long.parseLong(objectInstanceKey), childName, true, null);
            String nextGroupToApprove = null;
            String nextGroupKeyToApprove = null;
            String lastApproverFieldName = null;
            
            /*
             * get the keys from the first record and see if the children
             * have the field values to save approvers and if so get the name of the field
             * used to hold the ID
             */
            if (reqChildren.length > 0)
            {
                Map childObj = reqChildren[0];
                Set<String> ckeys = childObj.keySet();
                Iterator<String> ci = ckeys.iterator();
                while(ci.hasNext())
                {
                    String k = ci.next();
                    if (k.contains("LASTAPPROVER_ID"))
                    {
                        lastApproverFieldName = k;
                        break;
                    }
                }
            }
            
            if (lastApproverFieldName == null)
            {
                setDefaults();
                return "SUCCESS";
            }
            
            /*
             * find the first child record with a null aprover id
             * and get its record key.
             */
            for(int c=0;c<reqChildren.length;c++)
            {
                Map childObj = reqChildren[c];
                String lastId = (String)childObj.get(lastApproverFieldName);
                if (lastId == null || lastId.trim().length() == 0)
                {
                    nextGroupKeyToApprove = (String)childObj.get(childName + "_KEY");
                    nextGroupToApprove = (String)childObj.get(groupField);
                    break;
                }
            }
            
            if (nextGroupToApprove == null || nextGroupToApprove.trim().length() == 0)
            {
                logger.error("Null Group Found for instance " + objectInstanceKey);
                setDefaults();
                return "SUCCESS";
            }
            
            /*
             * update the parent record so we know which group we are currently waiting for approval on
             */
            Map tmpMap = new HashMap();
            tmpMap.put(groupKeyFieldName,nextGroupKeyToApprove);
            logger.debug("Setting Next Group Key " + tmpMap);
            formUtils.setObjectFormValues(Long.parseLong(objectInstanceKey), tmpMap);
            
            // go find someone to assign based on business logic.
            assigGroupToApprover(resourceName,nextGroupToApprove);
                
            return "SUCCESS";
            
        }
        catch(Exception e)
        {
            logger.error("Error",e);
        }
        return "FAILURE";
        
    }
    
    /*
     * do a lookup to find if there is an approver for the Resource/Role
     */
    private void assigGroupToApprover(String resourceName,String nextGroupToApprove)
    {
        
        setDefaults();
    }
    
    /*
     * get the cild form name for this resource via the lookup table
     */
    private String getChildFormNameForResource(String resourceName)
    {
        try
        {
            OIMLookupUtilities lookupUtils = OIMUtilityGetter.getLookupUtils(fwLogger, lookupOps, database);
            String childName = lookupUtils.getLookupValue(resourceName, "Lookup.BofA.Resource.ChildObjectForms");
            return childName;
        }
        catch(Exception e)
        {
            logger.error("Error getChildFormNameForResource",e);
        }
        return null;
    }
    
    /*
     * get the cild form name for this resource via the lookup table
     */
    private String getChildProcessFormNameForResource(String resourceName)
    {
        try
        {
            OIMLookupUtilities lookupUtils = OIMUtilityGetter.getLookupUtils(fwLogger, lookupOps, database);
            String childName = lookupUtils.getLookupValue(resourceName, "Lookup.BofA.Resource.ChildProcessForms");
            return childName;
        }
        catch(Exception e)
        {
            logger.error("Error getChildFormNameForResource",e);
        }
        return null;
    }
    
    /*
     * get the cild form name for this resource via the lookup table
     */
    private List getProvisioningGroups() throws Exception
    {
        try
        {
            OIMLookupUtilities lookupUtils = OIMUtilityGetter.getLookupUtils(fwLogger, lookupOps, database);
            Map provisioningGroups = lookupUtils.getLookupValues("Lookup.BofA.Provisioning.Groups");
            List provisioningList = new ArrayList(provisioningGroups.entrySet());
            return provisioningList;
        }
        catch(Exception e)
        {
            logger.error("Error getChildFormNameForResource",e);
            throw e;
        }
    }
    
    /*
     * get the field in the child table that represents the groupname value via lookup
     */
    private String getChildGroupField(String resourceName) throws Exception
    {
        try
        {
            OIMLookupUtilities lookupUtils = OIMUtilityGetter.getLookupUtils(fwLogger, lookupOps, database);
            String groupName = lookupUtils.getLookupValue(resourceName, "lookup.BofA.resource.childformmapping");
            return groupName;
        }
        catch(Exception e)
        {
            logger.error("Error getChildFormNameForResource",e);
            throw e;
        }
    }
    
    /*
     * get the values for the approver type and id that are in the process form
     * we re map them to our values
     */
    private Map getApprovalFields(long processInstanceKey) throws Exception
    {
        logger.debug("getApprovalFields for PIK:" + processInstanceKey);
        try
        {
            tcResultSet results = formInstOps.getProcessFormData(processInstanceKey);
            if (results == null || results.getRowCount() == 0)
            {
                logger.error("No Record found for PIK:" + processInstanceKey);
                return null;
            }
            
            String[] colNames = results.getColumnNames();
            HashMap fldMap = new HashMap();
            for(int i=0;i<results.getRowCount();i++)
            {
                
                results.goToRow(i);
                for(int c=0;c<colNames.length;c++)
                {
                    String n = colNames[c];
                    logger.debug("Checking Field:" + n);
                    if (n.contains("LASTAPPROVER_TYPE"))
                    {
                        fldMap.put("LASTAPPROVER_TYPE",results.getStringValue(n));
                    }
                    if (n.contains("LASTAPPROVER_ID"))
                    {
                        fldMap.put("LASTAPPROVER_ID",results.getStringValue(n));
                    }
                }
            }
            if (fldMap.isEmpty())
            {
                logger.error("No Approval fields found for PIK:" + processInstanceKey);
                return null;
            }
            return fldMap;
            
        }
        catch(Exception e)
        {
            logger.error("Error Getting Approval Fields",e);
            throw e;
        }
        
    }
    
    private Stack getChildApprovers(long processInstanceKey,String resourceName) throws Exception
    {
        Stack approverList = new Stack();
        try
        {
            OIMFormUtilities formUtils = OIMUtilityGetter.getFormUtilities(fwLogger, formDefOps, formInstOps);
            String childName = this.getChildProcessFormNameForResource(resourceName);
            logger.debug("Get Process data for key " + processInstanceKey);
            logger.debug("Resource " + resourceName + " childName " + childName);
            Map[] children = formUtils.getProcessFormChildValues(processInstanceKey,childName,false, null);
            
            if (children == null || children.length == 0)
            {
                logger.error("No Record found for PIK:" + processInstanceKey);
                return null;
            }
            
            String[] colNames = new String[children[0].keySet().size()];
            colNames = (String[])children[0].keySet().toArray();
            HashMap fldMap = new HashMap();
            for(int i=0;i<children.length;i++)
            {
                Map child = children[i];
                for(int c=0;c<colNames.length;c++)
                {
                    String n = colNames[c];
                    logger.debug("Checking Field:" + n);
                    if (n.contains("LASTAPPROVER_TYPE"))
                    {
                        fldMap.put("LASTAPPROVER_TYPE",child.get(n));
                    }
                    if (n.contains("LASTAPPROVER_ID"))
                    {
                        fldMap.put("LASTAPPROVER_ID",child.get(n));
                    }
                }
                if (!fldMap.isEmpty())
                {
                    approverList.push(fldMap);
                    fldMap = new HashMap();
                }
            }
            
            return approverList;
            
        }
        catch(Exception e)
        {
            logger.error("Error Getting Approval Fields",e);
            throw e;
        }
        
    }
    
    /*
     * returns a map of selected fields for a user based on a search criteria
     */
    private Map getUserDataSelected(Map SearchFor, List selectedFields) throws Exception {

        logger.debug("Entering userUtilities.getUsersData()");
        boolean HideEncrypted = true;
        Map result = new HashMap();
        
        try
        {
            tcUserOperationsIntf userOp =
                    (tcUserOperationsIntf) tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcUserOperationsIntf");
            logger.debug("Find Users " + SearchFor + " Returns " + selectedFields);
            String[] returnFields = new String[selectedFields.size()];
            selectedFields.toArray(returnFields);
            tcResultSet results = userOp.findUsersFiltered(SearchFor, returnFields);

            if (results == null || results.getRowCount() > 1) {
                logger.error("Too Many Users Found " + SearchFor);
                throw new Exception("Duplicate Records Found");
            }

            if (results == null || results.getRowCount() == 0)
            {
                logger.error("User Not Found " + SearchFor);
                throw new Exception("User Not Found");
            }
            
            results.goToRow(0);
            String[] columns = results.getColumnNames();

            for (int j = 0; j < columns.length; j++) {
                result.put(columns[j], results.getStringValue(columns[j]));
            }
        }
        catch(Exception e)
        {
            logger.error("Error Getting User Record",e);
            throw e;
        }

        logger.debug("Exiting userUtilities.getUsersData()");
        return result;

    }
    
    /*
     * find the key for a group that will be used for an assignment
     */
    private String findGroup(String name) throws Exception {

        logger.debug("Entering groupUtilities.findGroups()");
        tcGroupOperationsIntf groupOp =
                    (tcGroupOperationsIntf) tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcGroupOperationsIntf");

        Map searchFor = new HashMap();
        searchFor.put("Groups.Group Name", name);
        tcResultSet results = groupOp.findGroups(searchFor);

        String result = null;
        
        if (results != null && results.getRowCount() > 1)
        {
            logger.error("Duplicate Groups Found for " + name);
            throw new Exception("Too Many Groups");
        }

        for (int i = 0; i < results.getRowCount(); i++) {
            results.goToRow(i);
            result = results.getStringValue("Groups.Key");
        }

        logger.debug("Exiting groupUtilities.findGroups()");
        return result;

    }

}
